#include "TcpConnection.h"
#include "../base/log/Logging.h"
#include "tcp/SocketUtils.h"


TcpConnection::TcpConnection(EventLoop *loop,
                             std::string name,
                             int sockfd,
                             const InetAddress &localAddr,
                             const InetAddress &peerAddr) :
        loop_(loop),
        name_(std::move(name)),
        state_(kConnecting),
        reading_(true),
        socket_(new Socket(sockfd)),
        channel_(new Channel(loop, sockfd)),
        localAddr_(localAddr),
        peerAddr_(peerAddr) {
    channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
    channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));
    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
    // 注册好连接对应的回调函数
    socket_->setKeepAlive(true);
}

TcpConnection::~TcpConnection() {
    assert(state_ == kDisconnected);
}

void TcpConnection::handleRead(Timestamp receiveTime) {
    loop_->assertInLoopThread();
    int savedErrno = 0;
    ssize_t n = inputBuffer_.readFd(socket_->fd(), &savedErrno);
    // 读取客户端连接中的的消息, 并装入Buffer缓冲区
    if (n > 0) {
        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
        // 调用事件回调处理, 如果用户需要发送消息需要经过send来进行发送
    } else if (n == 0) {
        handleClose();
        // 当读取不到对应的数据的时候, 判断对方客户端关闭连接
    } else {
        errno = savedErrno;
        handleError();
    }
}

void TcpConnection::handleWrite() {
    loop_->assertInLoopThread();
    if (channel_->isWriting()) {
        ssize_t n = ::write(channel_->fd(),
                            outputBuffer_.peek(),
                            outputBuffer_.readableBytes());
        // 向文件描述符中写入对应的消息
        if (n > 0) {
            outputBuffer_.retrieve(n);
            if (outputBuffer_.readableBytes() == 0) {
                channel_->disableWriting();
                // 当输出缓冲区为 0 的时候, 取消关注可写事件
                if (writeCompleteCallback_) {
                    // 写入完毕后如果需要追加什么消息就会自动调用
                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
                }
                if (state_ == kDisconnecting) {
                    // 如果当前的状态是关闭中, 开启半关闭状态, 不允许文件描述符进行写入操作
                    shutdownInLoop();
                }
            }
        } else {
            LOG_SYSERR << "TcpConnection::handleWrite";
            if (state_ == kDisconnecting) { shutdownInLoop(); }
        }
    } else {
        LOG_TRACE << "Connection fd = " << channel_->fd()
                  << " 已经关闭, 无法继续写入";
    }
}

void TcpConnection::handleClose() {
    loop_->assertInLoopThread();
    LOG_TRACE << "fd = " << channel_->fd() << " 的客户端[" << peerAddr_.toIpPort() << "]关闭";
    assert(state_ == kConnected || state_ == kDisconnecting);
    // close释放客户端连接的操作是交给socket_的析构函数, 我们要做的就是在socket_析构之前将一系列对应的组件释放
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr guardThis(shared_from_this());
    connectionCallback_(guardThis);
    // 通知连接关闭
    closeCallback_(guardThis);
    // 调用关闭连接
}

void TcpConnection::handleError() {
    int err = sockets::getSocketError(channel_->fd());
    LOG_ERROR << "TcpConnection::handleError [" << name_ << "] 原因" << strerror_tl(err);
}

void TcpConnection::sendInLoop(const string &message) {
    sendInLoop(message.data(), message.size());
}

void TcpConnection::sendInLoop(const void *message, size_t len) {
    loop_->assertInLoopThread();
    ssize_t nwrote = 0;
    // 已发送数据量
    size_t remaining = len;
    // 剩余未发送的数据量
    bool faultError = false;
    if (state_ == kDisconnected) {
        LOG_WARN << "连接已销毁, 放弃发送信息";
        return;
    }
    /* 这里是输出缓冲区没有缓存的数据的情况下才会直接调用去写入 */
    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
        // 当前文件描述符没有关心可写事件 && 输出缓冲区没有待发送的数据
        nwrote = sockets::write(channel_->fd(), message, len);
        if (nwrote >= 0) {
            remaining = len - nwrote;
            if (remaining == 0 && writeCompleteCallback_) {
                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
            }
        } else // nwrote < 0
        {
            nwrote = 0;
            if (errno != EWOULDBLOCK) {
                LOG_SYSERR << "TcpConnection::sendInLoop失败";
                if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?
                {
                    faultError = true;
                }
            }
        }
    }

    assert(remaining <= len);

    /* 当前输出缓冲区存在数据的情况下调用 */
    if (!faultError && remaining > 0) {
        size_t oldLen = outputBuffer_.readableBytes();
        // 获取输出缓冲区未发送的数据量
        if (oldLen + remaining >= highWaterMark_
            && oldLen < highWaterMark_
            && highWaterMarkCallback_) {
            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
            // 未发送的数据低于缓冲区最高水位, 而总的待发送数据高于最高水位的时候, 调用超过水位的处理函数
        }
        outputBuffer_.append(static_cast<const char *>(message) + nwrote, remaining);
        // 将待发送的数据写入到输出缓冲区中等待发送
        if (!channel_->isWriting()) {
            channel_->enableWriting();
            // 调用关心可写事件
        }
    }
}

void TcpConnection::shutdown() {
    if (state_ == kConnected) {
        setState(kDisconnecting);
        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

void TcpConnection::shutdownInLoop() {
    loop_->assertInLoopThread();
    if (!channel_->isWriting()) {
        // we are not writing
        socket_->shutdownWrite();
    }
}

bool TcpConnection::getTcpInfo(struct tcp_info *tcpi) const {
    return socket_->getTcpInfo(tcpi);
}

void TcpConnection::send(const void *message, size_t len) {
    // FIXME 记得解决下这里的void * 指针转化为string的问题
    const std::string data(static_cast<const char *>(message), len);
    send(data);
}

void TcpConnection::send(const string &message) {
    if (state_ == kConnected) {
        if (loop_->isInLoopThread()) {
            sendInLoop(message);
        } else {
            void (TcpConnection::*fp)(const std::string &message) = &TcpConnection::sendInLoop;
            loop_->runInLoop(
                    std::bind(fp,
                              this,     // FIXME
                              message));
        }
    }
}

void TcpConnection::send(Buffer *buf) {
    if (state_ == kConnected) {
        if (loop_->isInLoopThread()) {
            sendInLoop(buf->peek(), buf->readableBytes());
            // 先将数据发送完毕后再清空, 防止出问题
            buf->retrieveAll();
        } else {
            void (TcpConnection::*fp)(const std::string &message) = &TcpConnection::sendInLoop;
            loop_->runInLoop(
                    std::bind(fp,
                              this,     // FIXME
                              buf->retrieveAllAsString()));
        }
    }
}

void TcpConnection::setTcpNoDelay(bool on) {
    socket_->setTcpNoDelay(on);
}

void TcpConnection::connectionEstablished() {
    loop_->assertInLoopThread();
    assert(state_ == kConnecting);
    setState(kConnected);
    // channel_->tie(shared_from_this());
    channel_->enableReading();
    // 设定监听连接的可读事件, 这里没有注册
    connectionCallback_(shared_from_this());
    // 调用连接到达时的函数
}

void TcpConnection::connectDestroyed() {
    loop_->assertInLoopThread();
    // 这个是TcpConnection析构前调用的最后一个函数, 用于通知用户连接已断开
    if (state_ == kConnected) {
        setState(kDisconnected);
        channel_->disableAll();
        connectionCallback_(shared_from_this());
    }
    channel_->remove();
    // 通知用户连接已经断开, 同时将channel_在ioLoop中注销掉
}